#include "public_includes.h"
#include "keyframed_property.h"
#include "keyframe.h"
#include "keyframe_list.h"
#include "keyframe_range.h"
#include "interpolator.h"
#include "cex.h"
#include "private_includes.h"

typedef struct _vus_keyframed_property {
	vus_keyframe_list *keyframes;
	vus_keyframe_range range;
	vus_interpolator interpolator;
} vus_keyframed_property;

#ifdef NDEBUG
#define invariant(self)
#else
#define invariant(self) \
	assert(self != NULL); \
	assert(self->keyframes != NULL); \
	assert(self->interpolator != NULL); \
	assert(self->range.min <= self->range.max);
#endif

vus_keyframed_property *vus_keyframed_property_alloc() {
	vus_keyframed_property *output = (vus_keyframed_property *)malloc(sizeof(vus_keyframed_property));

	if (output == NULL)
		THROW(VUS_OUT_OF_MEMORY, "Out of memory.");

	output->keyframes = vus_keyframe_list_alloc();
	output->range.min = FLT_MIN;
	output->range.max = FLT_MAX;
	output->interpolator = vus_linear_interpolator;

	invariant(output);
	return output;
}

void vus_keyframed_property_free(vus_keyframed_property *self) {
	assert(self != NULL);

	vus_keyframe_list_free(self->keyframes);
	free(self);
}

vus_interpolator vus_keyframed_property_get_interpolator(vus_keyframed_property *self) {
	assert(self != NULL);

	return self->interpolator;
}

void vus_keyframed_property_set_interpolator(vus_keyframed_property *self, vus_interpolator value) {
	assert(self != NULL);

	// NULL translates to the default interpolator (linear)
	self->interpolator = value == NULL ? vus_linear_interpolator : value;

	invariant(self);
}

vus_keyframe_range vus_keyframed_property_get_range(vus_keyframed_property *self) {
	assert(self != NULL);

	return self->range;
}

void vus_keyframed_property_set_range(vus_keyframed_property *self, vus_keyframe_range value) {
	assert(self != NULL);
	assert(value.min <= value.max);

	self->range = value;

	invariant(self);
}

vus_keyframe_list *vus_keyframed_property_keyframes(vus_keyframed_property *self) {
	return self->keyframes;
}

uint vus_keyframed_property_lower_bound(vus_keyframed_property *self, long frame) {
	uint i = 0;
	assert(self != NULL);

	for (i = 0; i < vus_keyframe_list_count(self->keyframes); i++)
		if (vus_keyframe_list_get(self->keyframes, i).location > frame)
			return i == 0 ? 0 : i - 1;

	return i - 1;
}

float vus_keyframed_property_value(vus_keyframed_property *self, long frame) {
	uint i = vus_keyframed_property_lower_bound(self, frame); // self != NULL test done by this function - unnecessary here.
	uint keyframeCount = vus_keyframe_list_count(self->keyframes);
	vus_keyframe lowerBound, upperBound;

	lowerBound = vus_keyframe_list_get(self->keyframes, i);
	upperBound = vus_keyframe_list_get(self->keyframes, i + 1);

	if (frame <= lowerBound.location)
		return lowerBound.value;
	else if (frame >= upperBound.location)
		return upperBound.value;
	else {
		return self->interpolator(
			lowerBound.value, 
			upperBound.value,
			(frame - lowerBound.location) / (float)(upperBound.value - lowerBound.value));
	}
}
